package com.zlq.cn.retrofit;

import com.alibaba.fastjson.JSON;
import retrofit2.Call;
import retrofit2.CallAdapter;
import retrofit2.Retrofit;

import java.lang.annotation.Annotation;
import java.lang.reflect.Type;

/**
 * 同步执行适配器
 *
 * @author zhoucl
 */
public class SyncCallAdapterFactory extends CallAdapter.Factory {

    public static SyncCallAdapterFactory create() {
        return new SyncCallAdapterFactory();
    }

    @Override
    public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
        if (getRawType(returnType) == Call.class) {
            return null;
        }
        return new SyncCallAdapter(annotations, returnType);
    }

    static class SyncCallAdapter implements CallAdapter<Object, Object> {
        /**
         * 本来的返回类型
         */
        private final Type returnType;
        /**
         * 请求返回类型
         */
        private Type responseType;
        /**
         * 错误重试次数
         */
        private int retryCount = RequestConfig.DEFAULT_RETRY_COUNT;
        /**
         * 重试间隔
         */
        private long retryInterval = 0;
        /**
         * 是否取出data
         */
        private boolean resultGetData = false;
        /**
         * 是否校验返回code
         */
        private boolean checkResultCode = false;

        public SyncCallAdapter(Annotation[] annotations, Type returnType) {
            this.returnType = returnType;
            this.responseType = returnType;
            RequestConfig requestConfig = findAnnotation(annotations, RequestConfig.class);
            if (requestConfig != null) {
                retryCount = requestConfig.retryCount();
                checkResultCode = requestConfig.checkResultCode();
                retryInterval = requestConfig.unit().toMillis(requestConfig.retryInterval());
                Class<? extends IResult> resultClz = requestConfig.resultClz();
                // 不是不处理实现以及返回值跟配置的返回结果不一样则使用配置的返回结果做json解析
                if (resultClz != IResult.class && getRawType(returnType) != requestConfig.resultClz()) {
                    responseType = resultClz;
                    resultGetData = true;
                }
            }
        }

        @Override
        public Type responseType() {
            return responseType;
        }

        @Override
        public Object adapt(Call<Object> call) {
            try {
                return execute(call);
            } catch (Exception e) {
                // code校验不通过的异常直接结束
                if (e instanceof IllegalArgumentException) {
                    throw (IllegalArgumentException) e;
                } else {
                    // 错误重试
                    if (retryCount > 1) {
                        retryCount--;
                        if (retryInterval > 0) {
                            try {
                                Thread.sleep(retryInterval);
                            } catch (InterruptedException ie) {
                                ie.printStackTrace();
                            }
                        }
                        return adapt(call);
                    }
                    throw new RuntimeException(e);
                }
            }
        }

        /**
         * 执行请求并处理结果
         *
         * @param call 请求执行器
         * @return 请求结果
         * @throws Exception 请求异常
         */
        private Object execute(Call<Object> call) throws Exception {
            Object body = call.execute().body();
            if (body instanceof IResult) {
                // 判断code并且抛异常
                IResult result = (IResult) body;
                if (checkResultCode && !result.isOk()) {
                    throw new IllegalArgumentException(result.getErrorMsg());
                }
                // 返回值直接就是具体泛型的情况直接取出并且重新解析
                if (resultGetData) {
                    Object data = result.getData();
                    body = JSON.parseObject(JSON.toJSONString(data), returnType);
                }
            }
            return body;
        }
    }

    /**
     * 寻找指定注解
     *
     * @param annotations 注解集合
     * @param cls         需要虚招打注解
     */
    static <T> T findAnnotation(Annotation[] annotations, Class<? extends Annotation> cls) {
        for (Annotation annotation : annotations) {
            if (cls.isInstance(annotation)) {
                return (T) annotation;
            }
        }
        return null;
    }
}
